const fs = require("fs");
const path = require("path");
const { minimatch } = require("minimatch");
const { type } = require("os");

module.exports = class UselessFilesCleanWebpackPlugin {
  constructor(options) {
    this.options = options;
    this.depFileList = [];
  }
  apply(compiler) {
    let _this = this;

    function handler(fileDependencies) {
      _this.findDepFile(fileDependencies);
      _this.doneFile();
    }

    if (
      compiler &&
      compiler.hooks &&
      compiler.hooks.compilation &&
      compiler.hooks.compilation.tap
    ) {
      compiler.hooks.afterEmit.tap(
        "UselessFilesCleanWebpackPlugin",
        (compilation) => {
          handler(Array.from(compilation.fileDependencies));
        }
      );
    } else {
      compiler.plugin("after-emit", (compilation, done) => {
        handler(compilation.fileDependencies);
        done();
      });
    }
  }

  findAllFile() {
    let { root, exclude = [] } = this.options;
    let fileList = [];
    traversal(root, function (filePath) {
      if (!exclude.some((pattern) => minimatch(filePath, pattern))) {
        fileList.push(path.resolve(filePath).toLowerCase());
      }
    });
    return fileList;
  }

  findDepFile(fileDependencies) {
    this.depFileList = fileDependencies.reduce((prev, filePath) => {
      if (!minimatch(filePath, "**/node_modules/**")) {
        prev.push(filePath.toLowerCase());
      }
      return prev;
    }, []);
  }

  findUnUsedFileList(depFileList, allFileList) {
    return allFileList.reduce((prev, filePath) => {
      if (!depFileList.includes(filePath)) {
        prev.push(filePath);
      }
      return prev;
    }, []);
  }
  delete(unUsedFileList) {
    unUsedFileList.forEach((element) => {
      fs.unlinkSync(element);
    });
  }

  log(unUsedFileList) {
    let { options } = this;
    let { out, log = false, clean = false } = options;
    // 运行成功
    // 查找信息
    // 删除信息
    let unUsedFileListLen = unUsedFileList.length;

    let logMessage = [
      ["useless-files-webpack-plugin success"],
      [
        "\x1B[32m%s \x1B[31m%d \x1B[32m%s \x1B[39m",
        "useless-files-clean-webpack-plugin find ",
        unUsedFileListLen,
        "useless files",
      ],
    ];

    if (unUsedFileListLen) {
      unUsedFileList.forEach((unUsedFile) => {
        logMessage.push(["- " + unUsedFile]);
      });

      if (clean) {
        logMessage.push([
          "\x1B[32m%s\x1B[39m",
          `delete ${unUsedFileListLen} useless files success `,
        ]);
      }
    }

    logMessage.push(["useless-files-webpack-plugin success"]);

    let logger = {
      console: (logMessage) => {
        let group = logMessage.shift(),
          groupEnd = logMessage.pop();
        console.group.apply(console, group);
        logMessage.forEach((message) => console.log.apply(console, message));
        console.groupEnd.apply(console, groupEnd);
      },
      file: (logMessage) => {
        let txt = logMessage
          .map((message) => {
            if (message.length > 1) {
              message.shift();
            }
            return message.join(" ");
          })
          .join("\r\n");

        fs.writeFileSync(path.join(out, "log.txt"), txt, "utf-8");
      },
    };

    if (log) {
      let logFn = logger[log];
      logFn && typeof logFn === "function" && logFn(logMessage);
    }
  }

  doneFile() {
    let { depFileList, options } = this;
    let { out, log = false, clean = false } = options;
    let allFileList = this.findAllFile();
    let unUsedFileList = this.findUnUsedFileList(depFileList, allFileList);

    if (out != "log") {
      fs.writeFileSync(
        path.join(out, "allFileList.json"),
        JSON.stringify(allFileList, null, 4),
        "utf-8"
      );

      fs.writeFileSync(
        path.join(out, "depFileList.json"),
        JSON.stringify(depFileList, null, 4),
        "utf-8"
      );

      fs.writeFileSync(
        path.join(out, "unUsedFileList.json"),
        JSON.stringify(unUsedFileList, null, 4),
        "utf-8"
      );
    }

    if (clean) {
      this.delete(unUsedFileList);
    }

    this.log(unUsedFileList);
  }
};

function traversal(entry, callback) {
  let files = fs.readdirSync(entry);
  files.forEach((file) => {
    let filePath = path.join(entry, file);
    if (isDir(filePath)) {
      traversal(filePath, callback);
    } else {
      callback && callback(filePath);
    }
  });
}

function isDir(filepath) {
  //判断是否是文件夹 Boolean
  let stat = fs.statSync(filepath);
  return stat.isDirectory();
}
